#ifndef ALLOCATOR_DEFINES
#define ALLOCATOR_DEFINES

#include<iostream>
#include<cstdlib>
#include<cstring>
#include "memlib.h"
#include <assert.h>

/* Block header structure is as follows:
 * Allocated:
 *  - One size_t at start
 *  - One size_t at end
 * Both indicating the size of the block, to allow for O(1) coalescing.
 *
 * Free:
 *  - One size_t at start
 *  - One void * for pointer to previous free block
 *  - One void * for pointer to next free block
 *  - One size_t at end
 * Low bit of size_t representing block size indicates whether or not the block is in use.
 * The (LOG_MIN_SIZE - 1) bits are available for such status information :-D
 */


#define START_SIZE_ADDR(blockptr) (size_t*)(blockptr)
#define START_SIZE(blockptr) (*(START_SIZE_ADDR(blockptr)) & ~(size_t)1)

#define END_SIZE_ADDR(blockptr) (size_t*)((char *)blockptr + START_SIZE(blockptr) - sizeof(size_t))
#define END_SIZE(blockptr) (*(END_SIZE_ADDR(blockptr)) & ~(size_t)1)

#define PREV_PTR_ADDR(blockptr) (void **)(((char *)blockptr + sizeof(size_t)))
#define PREV_PTR(blockptr) (*PREV_PTR_ADDR(blockptr))

#define NEXT_PTR_ADDR(blockptr) (void **)(((char *)blockptr + sizeof(size_t) + sizeof(void *)))
#define NEXT_PTR(blockptr) (*NEXT_PTR_ADDR(blockptr))

#define LIST_INSERT_HEAD(headptr, blockptr) do { \
if (*(headptr) == NULL) { \
  *NEXT_PTR_ADDR((blockptr)) = NULL; \
} else { \
  *NEXT_PTR_ADDR((blockptr)) = *(headptr); \
  *PREV_PTR_ADDR(*(headptr)) = (blockptr); \
} \
*PREV_PTR_ADDR((blockptr)) = NULL; \
*(headptr) = (blockptr); \
} while (0)

#define LIST_REMOVE_HEAD(headptr) do { \
if (NEXT_PTR(*(headptr)) != NULL) { \
  *PREV_PTR_ADDR(NEXT_PTR(*(headptr))) = NULL; \
  *(headptr) = NEXT_PTR(*(headptr)); \
} else { \
  *(headptr) = NULL; \
} \
} while (0)

#define LIST_REMOVE(blockptr) do { \
*NEXT_PTR_ADDR(PREV_PTR(blockptr)) = NEXT_PTR(blockptr); \
if (!IS_TAIL(blockptr)) { \
  *PREV_PTR_ADDR(NEXT_PTR(blockptr)) = PREV_PTR(blockptr); \
} \
} while (0)

#define IS_FREE(blockptr) (1 & *START_SIZE_ADDR(blockptr))
#define IS_HEAD(blockptr) (NULL == PREV_PTR(blockptr))
#define IS_TAIL(blockptr) (NULL == NEXT_PTR(blockptr))

#define SET_FREE(size) ((size) | (size_t)1)
#define UNSET_FREE(size) ((size) & ~(size_t)1)

#define FREE_LIST_INDEX(size) (logbase2(size) - LOG_MIN_SIZE)

#define HEADER_SIZE (sizeof(size_t) * 2)
#define MIN_SIZE (HEADER_SIZE + sizeof(void *) * 2)
//Update with above two defines. Static evaluations of logbase2 weren't compilable :-/
#define LOG_MIN_SIZE 5
#define NUM_FREE_LISTS 30 - LOG_MIN_SIZE
//Same log_min_size applies, but the thread caches are arbitrarily smaller.
#define NUM_THREAD_FREE_LISTS 8

//Garbage collect thread caches at this size (40KB per thread)
#define THREAD_GC_THRESHOLD 0xA000

#define MIN_SBRK mem_pagesize()

//It turns out this makes minimal difference beyond 1 or 2 for the given traces
#define MAX_COALESCENCE 3

#endif
